#!/usr/bin/env sh
set -e

kubectl=kubectl
version=1.10.0
generator=""
node=""
nodefaultctx=0
nodefaultns=0
container_cpu="${KUBECTL_NODE_SHELL_POD_CPU:-100m}"
container_memory="${KUBECTL_NODE_SHELL_POD_MEMORY:-256Mi}"
volumes="[]"
volume_mounts="[]"
x_mode=0
labels="${KUBECTL_NODE_SHELL_LABELS}"
pod_running_timeout="${KUBECTL_NODE_SHELL_POD_RUNNING_TIMEOUT:-1m}"

if [ -t 0 ]; then
  tty=true
else
  tty=false
fi
while [ $# -gt 0 ]; do
  key="$1"

  case $key in
  -v | --version)
    echo "kubectl-node-shell $version"
    exit 0
    ;;
  --context)
    nodefaultctx=1
    kubectl="$kubectl --context $2"
    shift
    shift
    ;;
  --kubecontext=*)
    nodefaultctx=1
    kubectl="$kubectl --context=${key##*=}"
    shift
    ;;
  --kubeconfig)
    kubectl="$kubectl --kubeconfig $2"
    shift
    shift
    ;;
  --kubeconfig=*)
    kubectl="$kubectl --kubeconfig=${key##*=}"
    shift
    ;;
  -n | --namespace)
    nodefaultns=1
    kubectl="$kubectl --namespace $2"
    shift
    shift
    ;;
  --namespace=*)
    nodefaultns=1
    kubectl="$kubectl --namespace=${key##*=}"
    shift
    ;;
  -x)
    x_mode=1
    volumes='[{"hostPath":{"path":"/","type":""},"name":"host-root"}]'
    volume_mounts='[{"mountPath":"/host","name":"host-root"}]'
    shift
    ;;
  --)
    shift
    break
    ;;
  *)
    if [ -z "$node" ]; then
      node="${1#node/}"
      shift
    else
      echo "exactly one node required"
      exit 1
    fi
    ;;
  esac
done

if [ -z "$node" ]; then
  echo "Please specify node name"
  exit 1
fi

if [ -z "$KUBERNETES_PORT" ]; then
  # Set the default context and namespace to avoid situations where the user switch them during the build process
  [ "$nodefaultctx" = 1 ] || kubectl="$kubectl --context=$(${kubectl} config current-context)"
  [ "$nodefaultns" = 1 ] || kubectl="$kubectl --namespace=$(${kubectl} config view --minify --output 'jsonpath={.contexts..namespace}')"
fi

# Check the node and retrieve the node OS label
os="$($kubectl get node $node -o jsonpath="{.metadata.labels.kubernetes\.io/os}" || exit 1)"

# Set pod configuration per operating system
if [ "$os" = "windows" ]; then
  image="${KUBECTL_NODE_SHELL_IMAGE_WINDOWS:-mcr.microsoft.com/oss/kubernetes/windows-host-process-containers-base-image:v1.0.0}"
  name="pwsh"
  pod="${name}-$(env LC_ALL=C tr -dc a-z0-9 </dev/urandom | head -c 6)"
  cmd_start='"cmd.exe", "/c", "powershell.exe", "-nol"'
  cmd_arg_prefix=', "-Command"'
  cmd_default=''
  security_context='{"privileged":true,"windowsOptions":{"hostProcess":true,"runAsUserName":"NT AUTHORITY\\SYSTEM"}}'
else # If the OS isn't windows, assume linux
  image="${KUBECTL_NODE_SHELL_IMAGE:-docker.io/library/alpine}"
  name="nsenter"
  pod="${name}-$(env LC_ALL=C tr -dc a-z0-9 </dev/urandom | head -c 6)"
  cmd_start='"nsenter", "--target", "1", "--mount", "--uts", "--ipc", "--net", "--pid"'
  cmd_arg_prefix=', "--"'
  cmd_default=', "bash", "-l"'
  security_context='{"privileged":true}'
fi

# Build the container command
if [ $# -gt 0 ]; then
  if [ "$x_mode" -eq 1 ]; then
    cmd='['
  else
    cmd="[ $cmd_start $cmd_arg_prefix,"
  fi
  c=""
  while [ $# -gt 0 ]; do
    cmd="${cmd}${c} \"$(echo "$1" | \
      awk '{gsub(/["\\]/,"\\\\&");gsub(/\x1b/,"\\u001b");printf "%s",last;last=$0"\\n"} END{print $0}' \
    )\""
    c=,
    shift
  done
  cmd="$cmd ]"
else
  if [ "$x_mode" = 1 ]; then
    cmd='null'
  else
    cmd="[ $cmd_start $cmd_default ]"
  fi
fi

# test if resource specification is required
resources_json='"resources": {
          "limits":   { "cpu": "'${container_cpu}'", "memory": "'${container_memory}'" },
          "requests": { "cpu": "'${container_cpu}'", "memory": "'${container_memory}'" }
        }'
$kubectl run --image "$image" "$pod" --dry-run=server 2>&1 | grep -q 'failed quota' || resources_json='"resources": {}'

overrides="$(
cat <<EOT
{
  "spec": {
    "nodeName": "$node",
    "hostPID": true,
    "hostNetwork": true,
    "containers": [
      {
        "securityContext": $security_context,
        "image": "$image",
        "name": "$name",
        "stdin": true,
        "stdinOnce": true,
        "tty": $tty,
        "command": $cmd,
        $resources_json,
        "volumeMounts": $volume_mounts
      }
    ],
    "tolerations": [
      { "key": "CriticalAddonsOnly", "operator": "Exists" },
      { "effect": "NoExecute",       "operator": "Exists" }
    ],
    "volumes": $volumes
  }
}
EOT
)"
# Support Kubectl <1.18
m=$(kubectl version --client -o yaml | awk -F'[ :"]+' '$2 == "minor" {print $3+0}')
if [ "$m" -lt 18 ]; then
  generator="--generator=run-pod/v1"
fi

trap "EC=\$?; $kubectl delete pod --wait=false $pod >&2 || true; exit \$EC" EXIT INT TERM

echo "spawning \"$pod\" on \"$node\"" >&2
$kubectl run --image "$image" --restart=Never --overrides="$overrides" --labels="$labels" --pod-running-timeout="$pod_running_timeout" $([ "$tty" = true ] && echo -t) -i "$pod" $generator
